🎖️GitЯра🎖️
Node / meshtastic / Meshtastic-Android / files / core / service / src / commonTest / kotlin / org / meshtastic / core / service / MeshServiceOrchestratorTest.kt
Displaying Raw • Download
core/service/src/commonTest/kotlin/org/meshtastic/core/service/MeshServiceOrchestratorTest.kt f5e5fb6da63ee85e64dbbe19a519a5a3deccd408 (f5e5fb6d) Text, 12.26 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.service
Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787dev.mokkery.MockMode
Tff7b72import T7ee787dev.mokkery.answering.returns
Tff7b72import T7ee787dev.mokkery.every
Tff7b72import T7ee787dev.mokkery.matcher.any
Tff7b72import T7ee787dev.mokkery.mock
Tff7b72import T7ee787dev.mokkery.verify
Tff7b72import T7ee787dev.mokkery.verify.VerifyMode.Companion.atLeast
Tff7b72import T7ee787dev.mokkery.verify.VerifyMode.Companion.exactly
Tff7b72import T7ee787dev.mokkery.verifySuspend
Tff7b72import T7ee787kotlinx.coroutines.ExperimentalCoroutinesApi
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableSharedFlow
Tff7b72import T7ee787kotlinx.coroutines.flow.MutableStateFlow
Tff7b72import T7ee787kotlinx.coroutines.test.UnconfinedTestDispatcher
Tff7b72import T7ee787org.meshtastic.core.common.database.DatabaseManager
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.Node
Tff7b72import T7ee787org.meshtastic.core.model.service.ServiceAction
Tff7b72import T7ee787org.meshtastic.core.repository.CommandSender
Tff7b72import T7ee787org.meshtastic.core.repository.MeshActionHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshConfigHandler
Tff7b72import T7ee787org.meshtastic.core.repository.MeshConnectionManager
Tff7b72import T7ee787org.meshtastic.core.repository.MeshMessageProcessor
Tff7b72import T7ee787org.meshtastic.core.repository.MeshRouter
Tff7b72import T7ee787org.meshtastic.core.repository.MeshServiceNotifications
Tff7b72import T7ee787org.meshtastic.core.repository.NodeManager
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioInterfaceService
Tff7b72import T7ee787org.meshtastic.core.repository.ServiceRepository
Tff7b72import T7ee787org.meshtastic.core.repository.TakPrefs
Tff7b72import T7ee787org.meshtastic.core.takserver.TAKMeshIntegration
Tff7b72import T7ee787org.meshtastic.core.takserver.TAKServerManager
Tff7b72import T7ee787org.meshtastic.proto.LocalModuleConfig
Tff7b72import T7ee787kotlin.test.Test
Tff7b72import T7ee787kotlin.test.assertFalse
Tff7b72import T7ee787kotlin.test.assertTrue
Tff7b72class T56d364MeshServiceOrchestratorTest Tb4b4b4{
Tff7b72private Tff7b72val Te6edf3radioInterfaceServiceTb4b4b4: Te6edf3RadioInterfaceService Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3serviceRepositoryTb4b4b4: Te6edf3ServiceRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3nodeManagerTb4b4b4: Te6edf3NodeManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3messageProcessorTb4b4b4: Te6edf3MeshMessageProcessor Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3commandSenderTb4b4b4: Te6edf3CommandSender Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3routerTb4b4b4: Te6edf3MeshRouter Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3actionHandlerTb4b4b4: Te6edf3MeshActionHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3meshConfigHandlerTb4b4b4: Te6edf3MeshConfigHandler Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3serviceNotificationsTb4b4b4: Te6edf3MeshServiceNotifications Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3takServerManagerTb4b4b4: Te6edf3TAKServerManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3takPrefsTb4b4b4: Te6edf3TakPrefs Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepository Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3databaseManagerTb4b4b4: Te6edf3DatabaseManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tff7b72private Tff7b72val Te6edf3connectionManagerTb4b4b4: Te6edf3MeshConnectionManager Tff7b72= Te6edf3mockTb4b4b4(Te6edf3MockModeTb4b4b4.Te6edf3autofillTb4b4b4)
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalCoroutinesApiTff7b72::Te6edf3classTb4b4b4)
Tff7b72private Tff7b72val Te6edf3testDispatcher Tff7b72= Te6edf3UnconfinedTestDispatcherTb4b4b4(Tb4b4b4)
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalCoroutinesApiTff7b72::Te6edf3classTb4b4b4)
Tff7b72private Tff7b72val Te6edf3dispatchers Tff7b72= Te6edf3CoroutineDispatchersTb4b4b4(Te6edf3io Tff7b72= Te6edf3testDispatcherTb4b4b4, Te6edf3main Tff7b72= Te6edf3testDispatcherTb4b4b4, Te6edf3default Tff7b72= Te6edf3testDispatcherTb4b4b4)
T8b949e/** Stubs the shared flow dependencies used by every test and returns an orchestrator. */
Tff7b72private Tff7b72fun Td2a8ffcreateOrchestratorTb4b4b4(
Te6edf3receivedDataTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3connectionErrorTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Tffa657StringTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3serviceActionTb4b4b4: Te6edf3MutableSharedFlowTff7b72<Te6edf3ServiceActionTff7b72> Tff7b72= Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3takEnabledFlowTb4b4b4: Te6edf3MutableStateFlowTff7b72<Tffa657BooleanTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)Tb4b4b4,
Te6edf3takRunningFlowTb4b4b4: Te6edf3MutableStateFlowTff7b72<Tffa657BooleanTff7b72> Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3MeshServiceOrchestrator Tb4b4b4{
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3receivedData Tb4b4b4} Te6edf3returns Te6edf3receivedData
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3connectionError Tb4b4b4} Te6edf3returns Te6edf3connectionError
Te6edf3every Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3serviceAction Tb4b4b4} Te6edf3returns Te6edf3serviceAction
Te6edf3every Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3meshPacketFlow Tb4b4b4} Te6edf3returns Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3meshConfigHandlerTb4b4b4.Te6edf3moduleConfig Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Te6edf3LocalModuleConfigTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3takPrefsTb4b4b4.Te6edf3isTakServerEnabled Tb4b4b4} Te6edf3returns Te6edf3takEnabledFlow
Te6edf3every Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3isRunning Tb4b4b4} Te6edf3returns Te6edf3takRunningFlow
Te6edf3every Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3inboundMessages Tb4b4b4} Te6edf3returns Te6edf3MutableSharedFlowTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3routerTb4b4b4.Te6edf3actionHandler Tb4b4b4} Te6edf3returns Te6edf3actionHandler
Te6edf3every Tb4b4b4{ Te6edf3nodeRepositoryTb4b4b4.Te6edf3myNodeInfo Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Tff7b72val Te6edf3takMeshIntegration Tff7b72=
Te6edf3TAKMeshIntegrationTb4b4b4(
Te6edf3takServerManager Tff7b72= Te6edf3takServerManagerTb4b4b4,
Te6edf3commandSender Tff7b72= Te6edf3commandSenderTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3meshConfigHandler Tff7b72= Te6edf3meshConfigHandlerTb4b4b4,
Te6edf3nodeRepository Tff7b72= Te6edf3nodeRepositoryTb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3MeshServiceOrchestratorTb4b4b4(
Te6edf3radioInterfaceService Tff7b72= Te6edf3radioInterfaceServiceTb4b4b4,
Te6edf3serviceRepository Tff7b72= Te6edf3serviceRepositoryTb4b4b4,
Te6edf3nodeManager Tff7b72= Te6edf3nodeManagerTb4b4b4,
Te6edf3messageProcessor Tff7b72= Te6edf3messageProcessorTb4b4b4,
Te6edf3router Tff7b72= Te6edf3routerTb4b4b4,
Te6edf3serviceNotifications Tff7b72= Te6edf3serviceNotificationsTb4b4b4,
Te6edf3takServerManager Tff7b72= Te6edf3takServerManagerTb4b4b4,
Te6edf3takMeshIntegration Tff7b72= Te6edf3takMeshIntegrationTb4b4b4,
Te6edf3takPrefs Tff7b72= Te6edf3takPrefsTb4b4b4,
Te6edf3databaseManager Tff7b72= Te6edf3databaseManagerTb4b4b4,
Te6edf3connectionManager Tff7b72= Te6edf3connectionManagerTb4b4b4,
Te6edf3dispatchers Tff7b72= Te6edf3dispatchersTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestStartWiresComponentsTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Te6edf3verify Tb4b4b4{ Te6edf3serviceNotificationsTb4b4b4.Te6edf3initChannelsTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3loadCachedNodeDBTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestTakServerStartsAndStopsWithPreferenceTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3takEnabledFlow Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3takRunningFlow Tff7b72= Te6edf3MutableStateFlowTb4b4b4(Tff7b72falseTb4b4b4)
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3takEnabledFlow Tff7b72= Te6edf3takEnabledFlowTb4b4b4, Te6edf3takRunningFlow Tff7b72= Te6edf3takRunningFlowTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
T8b949e// Toggle on
Te6edf3takEnabledFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72true
Te6edf3verify Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3startTb4b4b4(Te6edf3anyTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4}
T8b949e// Update mock state to reflect it's running
Te6edf3takRunningFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72true
T8b949e// Toggle off
Te6edf3takEnabledFlowTb4b4b4.Te6edf3value Tff7b72= Tff7b72false
Te6edf3verify Tb4b4b4{ Te6edf3takServerManagerTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestStartCallsSwitchActiveDatabaseTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3every Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3getDeviceAddressTb4b4b4(Tb4b4b4) Tb4b4b4} Te6edf3returns Ta5d6ff"Ta5d6fftcp:192.168.1.100Ta5d6ff"
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3verifySuspend Tb4b4b4{ Te6edf3databaseManagerTb4b4b4.Te6edf3switchActiveDatabaseTb4b4b4(Ta5d6ff"Ta5d6fftcp:192.168.1.100Ta5d6ff"Tb4b4b4) Tb4b4b4}
Te6edf3verify Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3connectTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestConnectionErrorForwardedToServiceRepositoryTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3connectionError Tff7b72= Te6edf3MutableSharedFlowTff7b72<Tffa657StringTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1Tb4b4b4)
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3connectionError Tff7b72= Te6edf3connectionErrorTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
T8b949e// Emit an error into the radio interface's connectionError flow
Te6edf3connectionErrorTb4b4b4.Te6edf3tryEmitTb4b4b4(Ta5d6ff"Ta5d6ffBLE connection lostTa5d6ff"Tb4b4b4)
Te6edf3verify Tb4b4b4{ Te6edf3serviceRepositoryTb4b4b4.Te6edf3setErrorMessageTb4b4b4(Ta5d6ff"Ta5d6ffBLE connection lostTa5d6ff"Tb4b4b4, Te6edf3SeverityTb4b4b4.Te6edf3WarnTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestServiceActionDispatchedToActionHandlerTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3serviceAction Tff7b72= Te6edf3MutableSharedFlowTff7b72<Te6edf3ServiceActionTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff1Tb4b4b4)
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3serviceAction Tff7b72= Te6edf3serviceActionTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3action Tff7b72= Te6edf3ServiceActionTb4b4b4.Te6edf3FavoriteTb4b4b4(Te6edf3NodeTb4b4b4(Te6edf3num Tff7b72= T79c0ff4T79c0ff2Tb4b4b4)Tb4b4b4)
Te6edf3serviceActionTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3actionTb4b4b4)
Te6edf3verifySuspend Tb4b4b4{ Te6edf3actionHandlerTb4b4b4.Te6edf3onServiceActionTb4b4b4(Te6edf3actionTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tf0883e@Test
Tff7b72fun Td2a8fftestStartIsIdempotentTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
T8b949e// Second call should be a no-op
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3assertTrueTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
T8b949e// Components should only be initialized once
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3serviceNotificationsTb4b4b4.Te6edf3initChannelsTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3verifyTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3loadCachedNodeDBTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3assertFalseTb4b4b4(Te6edf3orchestratorTb4b4b4.Te6edf3isRunningTb4b4b4)
Tb4b4b4}
T8b949e/**
* Regression test for a bug where `stop()` did not actually tear down the FromRadio collectors. Collectors were
* attached to an injected process-wide ServiceScope rather than a per-start scope, so `start() -> stop() ->
* start()` caused duplicate collectors and every FromRadio packet was handled 2x (then 3x, etc.).
*/
Tf0883e@Test
Tff7b72fun Td2a8fftestFromRadioCollectorsTornDownOnStopAndRestartedCleanlyOnStartTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3receivedData Tff7b72= Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff8Tb4b4b4)
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3receivedData Tff7b72= Te6edf3receivedDataTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3myNodeNum Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3packet1 Tff7b72= Te6edf3byteArrayOfTb4b4b4(T79c0ff1Tb4b4b4, T79c0ff2Tb4b4b4, T79c0ff3Tb4b4b4)
Te6edf3receivedDataTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3packet1Tb4b4b4)
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3messageProcessorTb4b4b4.Te6edf3handleFromRadioTb4b4b4(Te6edf3packet1Tb4b4b4, Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3packet2 Tff7b72= Te6edf3byteArrayOfTb4b4b4(T79c0ff4Tb4b4b4, T79c0ff5Tb4b4b4, T79c0ff6Tb4b4b4)
Te6edf3receivedDataTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3packet2Tb4b4b4)
T8b949e// After stop(), the collector must be gone - the handler should not be invoked for packet2.
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff0Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3messageProcessorTb4b4b4.Te6edf3handleFromRadioTb4b4b4(Te6edf3packet2Tb4b4b4, Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3packet3 Tff7b72= Te6edf3byteArrayOfTb4b4b4(T79c0ff7Tb4b4b4, T79c0ff8Tb4b4b4, T79c0ff9Tb4b4b4)
Te6edf3receivedDataTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3packet3Tb4b4b4)
T8b949e// After restart, a single fresh collector must process packet3 exactly once (not twice).
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3messageProcessorTb4b4b4.Te6edf3handleFromRadioTb4b4b4(Te6edf3packet3Tb4b4b4, Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
T8b949e/**
* Regression test for a channel-buffer-replay bug: the production [RadioInterfaceService] buffers inbound bytes in
* a process-lifetime `Channel(UNLIMITED)`. Between `stop()` and the next `start()`, any bytes that arrive sit in
* the channel and would be replayed to the fresh collector — prepending stale packets to the next session's
* firmware handshake. `start()` must call [RadioInterfaceService.resetReceivedBuffer] before attaching the
* collector.
*/
Tf0883e@Test
Tff7b72fun Td2a8fftestStartDrainsReceivedBufferBeforeAttachingCollectorTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Tb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3myNodeNum Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
T8b949e// resetReceivedBuffer must be invoked at least once per start() (twice total for two starts).
Te6edf3verifyTb4b4b4(Te6edf3atLeastTb4b4b4(T79c0ff2Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3radioInterfaceServiceTb4b4b4.Te6edf3resetReceivedBufferTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
T8b949e/** Additional regression: after many start/stop cycles, collectors must not accumulate. */
Tf0883e@Test
Tff7b72fun Td2a8fftestRepeatedStartStopDoesNotAccumulateCollectorsTb4b4b4(Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3receivedData Tff7b72= Te6edf3MutableSharedFlowTff7b72<Te6edf3ByteArrayTff7b72>Tb4b4b4(Te6edf3extraBufferCapacity Tff7b72= T79c0ff8Tb4b4b4)
Tff7b72val Te6edf3orchestrator Tff7b72= Te6edf3createOrchestratorTb4b4b4(Te6edf3receivedData Tff7b72= Te6edf3receivedDataTb4b4b4)
Te6edf3every Tb4b4b4{ Te6edf3nodeManagerTb4b4b4.Te6edf3myNodeNum Tb4b4b4} Te6edf3returns Te6edf3MutableStateFlowTb4b4b4(Tff7b72nullTb4b4b4)
Te6edf3repeatTb4b4b4(T79c0ff5Tb4b4b4) Tb4b4b4{
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3startTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3packet Tff7b72= Te6edf3byteArrayOfTb4b4b4(T79c0ff4T79c0ff2Tb4b4b4)
Te6edf3receivedDataTb4b4b4.Te6edf3tryEmitTb4b4b4(Te6edf3packetTb4b4b4)
T8b949e// Despite six total start() calls, only the most recent collector is live.
Te6edf3verifySuspendTb4b4b4(Te6edf3exactlyTb4b4b4(T79c0ff1Tb4b4b4)Tb4b4b4) Tb4b4b4{ Te6edf3messageProcessorTb4b4b4.Te6edf3handleFromRadioTb4b4b4(Te6edf3packetTb4b4b4, Tff7b72nullTb4b4b4) Tb4b4b4}
Te6edf3orchestratorTb4b4b4.Te6edf3stopTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Served by rngit 1.5.3 - Generated in 0.12s